package rsa

import (
	"common/config"
	"common/utils"
	"crypto"
	"crypto/rand"
	"crypto/rsa"
	"crypto/sha256"
	"crypto/x509"
	"encoding/base64"
	"encoding/json"
	"encoding/pem"
	"errors"
	"fmt"
	"hall/pay/entity"
	"io/ioutil"
	"math"
	"os"
)

var (
	signStr = "Zcxl918Smb"
)

// GenRsaKey
// bits 生成的公私钥对的位数，一般为 1024 或 2048
// privateKey 生成的私钥
// publicKey 生成的公钥
func GenRsaKey(bits int) {
	priKey, err2 := rsa.GenerateKey(rand.Reader, bits)
	if err2 != nil {
		panic(err2)
	}
	derStream := x509.MarshalPKCS1PrivateKey(priKey)
	block := &pem.Block{
		Type:  "PRIVATE KEY",
		Bytes: derStream,
	}
	prvKey := pem.EncodeToMemory(block)
	puKey := &priKey.PublicKey
	derPkix, err := x509.MarshalPKIXPublicKey(puKey)
	if err != nil {
		panic(err)
	}
	block = &pem.Block{
		Type:  "PUBLIC KEY",
		Bytes: derPkix,
	}
	pubKey := pem.EncodeToMemory(block)
	privateKey := string(prvKey)
	publicKey := string(pubKey)
	priFile, err := os.Create(config.Conf.Rsa.Private) // 也可以使用buffer, priBuf := bytes.NewBuffer(nil)
	if err != nil {
		panic(err)
	}

	defer priFile.Close()
	priFile.WriteString(privateKey)
	// 创建公钥文件
	pubFile, err := os.Create(config.Conf.Rsa.Public)
	if err != nil {
		panic(err)
	}
	defer pubFile.Close()
	pubFile.WriteString(publicKey)
	return
}

// EncryptBase64Rsa 使用 RSA 公钥加密数据, 返回加密后并编码为 base64 的数据
func EncryptBase64Rsa(originalData, publicKey string) (string, error) {
	block, _ := pem.Decode([]byte(publicKey))
	pubKey, parseErr := x509.ParsePKIXPublicKey(block.Bytes)
	if parseErr != nil {
		fmt.Println(parseErr)
		return "", errors.New("解析公钥失败")
	}
	encryptedData, err := rsa.EncryptPKCS1v15(rand.Reader, pubKey.(*rsa.PublicKey), []byte(originalData))
	return base64.StdEncoding.EncodeToString(encryptedData), err
}

// DecryptBase64Rsa 使用 RSA 私钥解密数据
func DecryptBase64Rsa(encryptedData, privateKey string) (string, error) {
	encryptedDecodeBytes, err := base64.StdEncoding.DecodeString(encryptedData)
	if err != nil {
		return "", err
	}
	block, _ := pem.Decode([]byte(privateKey))
	priKey, parseErr := x509.ParsePKCS8PrivateKey(block.Bytes)
	if parseErr != nil {
		fmt.Println(parseErr)
		return "", errors.New("解析私钥失败")
	}

	originalData, encryptErr := rsa.DecryptPKCS1v15(rand.Reader, priKey.(*rsa.PrivateKey), encryptedDecodeBytes)
	return string(originalData), encryptErr
}

// SignBase64
// originalData 签名前的原始数据
// privateKey RSA 私钥
func SignBase64(originalData, privateKey string) (string, error) {
	block, _ := pem.Decode([]byte(privateKey))
	priKey, parseErr := x509.ParsePKCS8PrivateKey(block.Bytes)
	if parseErr != nil {
		fmt.Println(parseErr)
		return "", errors.New("解析私钥失败")
	}

	// sha256 加密方式，必须与 下面的 crypto.SHA256 对应
	// 例如使用 sha1 加密，此处应是 sha1.New()，对应 crypto.SHA1
	hash := sha256.New()
	hash.Write([]byte(originalData))
	signature, err := rsa.SignPSS(rand.Reader, priKey.(*rsa.PrivateKey), crypto.SHA256, hash.Sum(nil), nil)

	return base64.StdEncoding.EncodeToString(signature), err
}

// VerySignWithBase64
// originalData 签名前的原始数据
// signData Base64 格式的签名串
// pubKey 公钥（需与加密时使用的私钥相对应）
// 返回 true 代表验签通过，反之为不通过
func VerySignWithBase64(originalData, signData, publicKey string) (bool, error) {
	sign, err := base64.StdEncoding.DecodeString(signData)
	if err != nil {
		return false, err
	}

	block, _ := pem.Decode([]byte(publicKey))
	pub, err1 := x509.ParsePKIXPublicKey(block.Bytes)
	if err1 != nil {
		return false, err1
	}
	// sha256 加密方式，必须与 下面的 crypto.SHA256 对应
	// 例如使用 sha1 加密，此处应是 sha1.New()，对应 crypto.SHA1
	hash := sha256.New()
	hash.Write([]byte(originalData))
	verifyErr := rsa.VerifyPKCS1v15(pub.(*rsa.PublicKey), crypto.SHA256, hash.Sum(nil), sign)
	return verifyErr == nil, nil
}

// PayVerifyRsa rsa 支付 加密 加签
func PayVerifyRsa(data *entity.Pay) ([]string, error) {
	publicKey, _ := ioutil.ReadFile(config.Conf.Rsa.Public)
	sign := utils.Md5Password(signStr)
	data.Sign = sign
	RsaData, _ := json.Marshal(data)
	initLen := 117
	RsaDataLen := len(RsaData)
	var RsaDataSub float64
	RsaDataSub = float64(RsaDataLen) / float64(initLen)
	RsaDataCeil := math.Ceil(RsaDataSub)
	var star, end int
	var EncryptData string
	var DataMap []string
	for i := 0; i < int(RsaDataCeil); i++ {
		star = initLen * i
		end = star + initLen
		if end > RsaDataLen {
			end = RsaDataLen
		}
		EncryptData, _ = EncryptBase64Rsa(string(RsaData[star:end]), string(publicKey))
		DataMap = append(DataMap, EncryptData)
	}
	return DataMap, nil
}
